# coding:utf-8
import copy
import json
from datetime import datetime
from enum import Enum
from typing import Union, List

import pandas as pd
import yaml
from faker import Faker

from common.settings import CONFIG_DIR


class FakerMethod(Enum):
    aba = {'name': 'ABA路由中转号'}
    address = {'name': '地址'}
    administrative_unit = {'name': '行政区划'}
    am_pm = {'name': '上午下午'}
    android_platform_token = {'name': '安卓平台令牌'}
    ascii_company_email = {'name': 'ASCII公司电子邮件'}
    ascii_email = {'name': 'ASCII电子邮件'}
    ascii_free_email = {'name': 'ASCII免费电子邮件'}
    ascii_safe_email = {'name': 'ASCII安全电子邮件'}
    bank = {'name': '银行'}
    bban = {'name': 'BBAN'}
    binary = {'name': '二进制', 'kwargs': {'length': 100}}
    boolean = {'name': '布尔值', 'kwargs': {'chance_of_getting_true': 50}}
    bothify = {'name': '生成带有特殊字符的字符串',
               'kwargs': {'text': 'Product Number: ????-########', 'letters': 'ABCDE'}}
    bs = {'name': 'BS'}
    building_number = {'name': '建筑号'}
    catch_phrase = {'name': 'catch phrase'}
    century = {'name': '世纪'}
    chrome = {'name': 'Chrome User Agent',
              'kwargs': {'version_from': 13, 'version_to': 63, 'build_from': 800, 'build_to': 899}}
    city = {'name': '城市'}
    city_name = {'name': '城市名'}
    city_suffix = {'name': '城市后缀'}
    color = {'name': '颜色'}
    color_name = {'name': '颜色名'}
    color_hsl = {'name': '色彩HSL值'}
    color_hsv = {'name': '色彩HSV值'}
    color_rgb = {'name': '色彩RGB值'}
    color_rgb_float = {'name': '色彩RGB浮点值'}
    company = {'name': '公司'}
    company_email = {'name': '公司电子邮件'}
    company_prefix = {'name': '公司前缀'}
    company_suffix = {'name': '公司后缀'}
    coordinate = {'name': '坐标'}
    credit_card_expire = {'name': '信用卡到期日'}
    credit_card_full = {'name': '信用卡全号'}
    credit_card_number = {'name': '信用卡号'}
    credit_card_provider = {'name': '信用卡提供商'}
    credit_card_security_code = {'name': '信用卡安全码'}
    cryptocurrency = {'name': '加密货币'}
    cryptocurrency_code = {'name': '加密货币代码'}
    cryptocurrency_name = {'name': '加密货币名称'}
    csv = {'name': 'CSV'}
    currency = {'name': '货币'}
    currency_code = {'name': '货币代码'}
    currency_name = {'name': '货币名称'}
    currency_symbol = {'name': '货币符号'}
    date = {'name': '日期'}
    date_between = {'name': '日期区间'}
    date_between_dates = {'name': '日期区间'}
    date_object = {'name': '日期对象'}
    date_of_birth = {'name': '出生日期'}
    date_this_century = {'name': '本世纪日期'}
    date_this_decade = {'name': '本十年日期'}
    date_this_month = {'name': '本月日期'}
    date_this_year = {'name': '本年日期'}
    date_time = {'name': '日期时间'}
    date_time_ad = {'name': '日期时间'}
    date_time_between = {'name': '日期时间区间'}
    date_time_between_dates = {'name': '日期时间区间'}
    date_time_this_century = {'name': '本世纪日期时间'}
    date_time_this_decade = {'name': '本十年日期时间'}
    date_time_this_month = {'name': '本月日期时间'}
    date_time_this_year = {'name': '本年日期时间'}
    day_of_month = {'name': '月份中的天'}
    day_of_week = {'name': '星期中的天'}
    dga = {'name': 'DGA'}
    district = {'name': '区'}
    domain_name = {'name': '域名'}
    domain_word = {'name': '域名词'}
    dsv = {'name': 'DSV'}
    ean = {'name': 'EAN'}
    ean13 = {'name': 'EAN-13'}
    ean8 = {'name': 'EAN-8'}
    email = {'name': '电子邮件'}
    emoji = {'name': '表情符号'}
    file_extension = {'name': '文件扩展名'}
    file_name = {'name': '文件名'}
    file_path = {'name': '文件路径'}
    firefox = {'name': 'Firefox User Agent'}
    name_ = {'name': '姓名'}
    first_name = {'name': '名'}
    first_name_female = {'name': '女名'}
    first_name_male = {'name': '男名'}
    first_name_nonbinary = {'name': '非二元性名'}
    first_romanized_name = {'name': '罗马化名'}
    free_email = {'name': '免费电子邮件'}
    free_email_domain = {'name': '免费电子邮件域名'}
    future_date = {'name': '未来日期'}
    future_datetime = {'name': '未来日期时间'}
    get_providers = {'name': '获取提供商'}
    get_words_list = {'name': '获取词列表'}
    hex_color = {'name': '十六进制颜色'}
    hexify = {'name': '生成十六进制字符串', 'kwargs': {'text': 'Product Number: ????-########', 'upper': True}}
    hostname = {'name': '主机名'}
    http_method = {'name': 'HTTP方法'}
    http_status_code = {'name': 'HTTP状态码'}
    iana_id = {'name': 'IANA ID'}
    iban = {'name': 'IBAN'}
    image = {'name': '图像'}
    image_url = {'name': '图像URL'}
    internet_explorer = {'name': 'Internet Explorer User Agent'}
    ios_platform_token = {'name': 'iOS平台令牌'}
    ipv4 = {'name': 'IPv4地址'}
    ipv4_network_class = {'name': 'IPv4网络类'}
    ipv4_private = {'name': 'IPv4私有地址'}
    ipv4_public = {'name': 'IPv4公共地址'}
    ipv6 = {'name': 'IPv6地址'}
    isbn10 = {'name': 'ISBN-10'}
    isbn13 = {'name': 'ISBN-13'}
    iso8601 = {'name': 'ISO-8601日期时间'}
    items = {'name': '项目'}
    job = {'name': '职业'}
    job_female = {'name': '女职业'}
    job_male = {'name': '男职业'}
    json = {'name': 'JSON'}
    json_bytes = {'name': 'JSON字节'}
    language_code = {'name': '语言代码'}
    language_name = {'name': '语言名称'}
    last_name = {'name': '姓'}
    last_name_female = {'name': '女姓'}
    last_name_male = {'name': '男姓'}
    last_name_nonbinary = {'name': '非二元性姓'}
    last_romanized_name = {'name': '罗马化姓'}
    latitude = {'name': '纬度'}
    latlng = {'name': '坐标'}
    lexify = {'name': '生成带有特殊字符的字符串',
              'kwargs': {'text': 'Product Number: ????-########', 'letters': 'ABCDE'}}
    license_plate = {'name': '车牌'}
    linux_platform_token = {'name': 'Linux平台令牌'}
    linux_processor = {'name': 'Linux处理器'}
    local_latlng = {'name': '本地坐标'}
    locale = {'name': '区域设置'}
    localized_ean = {'name': '本地化EAN'}
    localized_ean13 = {'name': '本地化EAN-13'}
    localized_ean8 = {'name': '本地化EAN-8'}
    location_on_land = {'name': '陆地上的位置'}
    longitude = {'name': '经度'}
    mac_address = {'name': 'MAC地址'}
    mac_platform_token = {'name': 'MAC平台令牌'}
    mac_processor = {'name': 'MAC处理器'}
    md5 = {'name': 'MD5'}
    mime_type = {'name': 'MIME类型'}
    month = {'name': '月份'}
    month_name = {'name': '月份名称'}
    msisdn = {'name': 'MSISDN'}
    name = {'name': '名称'}
    name_female = {'name': '女名称'}
    name_male = {'name': '男名称'}
    name_nonbinary = {'name': '非二元性名称'}
    nic_handle = {'name': 'NIC句柄'}
    nic_handles = {'name': 'NIC句柄列表'}
    null_boolean = {'name': '空布尔值'}
    numerify = {'name': '生成数字字符串', 'kwargs': {'text': 'Product Number: ????-########'}}
    opera = {'name': 'Opera User Agent'}
    paragraph = {'name': '段落'}
    paragraphs = {'name': '段落列表'}
    passport_dates = {'name': '护照有效期'}
    passport_dob = {'name': '护照出生日期'}
    passport_full = {'name': '护照全号'}
    passport_gender = {'name': '护照性别'}
    passport_number = {'name': '护照号码'}
    passport_owner = {'name': '护照持有人'}
    password = {'name': '密码'}
    past_date = {'name': '过去日期'}
    past_datetime = {'name': '过去日期时间'}
    phone_number = {'name': '电话号码'}
    phonenumber_prefix = {'name': '电话号码前缀'}
    port_number = {'name': '端口号'}
    postcode = {'name': '邮编'}
    prefix = {'name': '前缀'}
    prefix_female = {'name': '女性前缀'}
    prefix_male = {'name': '男性前缀'}
    prefix_nonbinary = {'name': '非二元性前缀'}
    pricetag = {'name': '价格标签'}
    profile = {'name': '个人资料'}
    province = {'name': '省'}
    psv = {'name': 'PSV'}
    pybool = {'name': 'Python布尔值'}
    pydecimal = {'name': 'Python十进制数'}
    pydict = {'name': 'Python字典'}
    pyfloat = {'name': 'Python浮点数'}
    pyint = {'name': 'Python整数'}
    pyiterable = {'name': 'Python可迭代对象'}
    pylist = {'name': 'Python列表'}
    pyobject = {'name': 'Python对象'}
    pyset = {'name': 'Python集合'}
    pystr = {'name': 'Python字符串'}
    pystr_format = {'name': 'Python格式化字符串'}
    pystruct = {'name': 'Python结构体'}
    pytimezone = {'name': 'Python时区'}
    pytuple = {'name': 'Python元组'}
    random_choices = {'name': '随机选择'}
    random_digit = {'name': '随机数字'}
    random_digit_above_two = {'name': '随机数字'}
    random_digit_not_null = {'name': '随机数字'}
    random_digit_not_null_or_empty = {'name': '随机数字'}
    random_digit_or_empty = {'name': '随机数字'}
    random_element = {'name': '随机元素'}
    random_elements = {'name': '随机元素列表'}
    random_int = {'name': '随机整数'}
    random_letter = {'name': '随机字母'}
    random_letters = {'name': '随机字母列表'}
    random_lowercase_letter = {'name': '随机小写字母'}
    random_number = {'name': '随机数字'}
    random_sample = {'name': '随机采样'}
    random_uppercase_letter = {'name': '随机大写字母'}
    randomize_nb_elements = {'name': '随机化元素数量'}
    rgb_color = {'name': 'RGB颜色'}
    rgb_css_color = {'name': 'RGB CSS颜色'}
    ripe_id = {'name': 'RIPE ID'}
    romanized_name = {'name': '罗马化名称'}
    safari = {'name': 'Safari User Agent'}
    safe_color_name = {'name': '安全颜色名'}
    safe_domain_name = {'name': '安全域名'}
    safe_email = {'name': '安全电子邮件'}
    safe_hex_color = {'name': '安全十六进制颜色'}
    sbn9 = {'name': 'SBN-9'}
    sentence = {'name': '句子'}
    sentences = {'name': '句子列表'}
    sha1 = {'name': 'SHA-1'}
    sha256 = {'name': 'SHA-256'}
    simple_profile = {'name': '简单个人资料'}
    slug = {'name': 'Slug'}
    ssn = {'name': 'SSN'}
    street_address = {'name': '街道地址'}
    street_name = {'name': '街道名'}
    street_suffix = {'name': '街道后缀'}
    suffix = {'name': '后缀'}
    suffix_female = {'name': '女性后缀'}
    suffix_male = {'name': '男性后缀'}
    suffix_nonbinary = {'name': '非二元性后缀'}
    swift = {'name': 'SWIFT'}
    swift11 = {'name': 'SWIFT-11'}
    swift8 = {'name': 'SWIFT-8'}
    tar = {'name': 'TAR'}
    text = {'name': '文本', 'kwargs': {'max_nb_chars': 200}}
    texts = {'name': '文本列表'}
    time = {'name': '时间',
            'kwargs': {'pattern': '%Y-%m-%d %H:%M:%S', 'end_datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S')}}
    time_delta = {'name': '时间差'}
    time_object = {'name': '时间对象'}
    time_series = {'name': '时间序列'}
    timezone = {'name': '时区'}
    tld = {'name': 'TLD'}
    tsv = {'name': 'TSV'}
    unix_device = {'name': 'UNIX设备'}
    unix_partition = {'name': 'UNIX分区'}
    unix_time = {'name': 'UNIX时间'}
    upc_a = {'name': 'UPC-A'}
    upc_e = {'name': 'UPC-E'}
    uri = {'name': 'URI'}
    uri_extension = {'name': 'URI扩展名'}
    uri_page = {'name': 'URI页面'}
    uri_path = {'name': 'URI路径'}
    url = {'name': 'URL'}
    user_name = {'name': '用户名'}
    uuid4 = {'name': 'UUID4'}
    vin = {'name': 'VIN'}
    windows_platform_token = {'name': 'Windows平台令牌'}
    word = {'name': '词'}
    words = {'name': '词列表'}
    xml = {'name': 'XML',
           'kwargs': {'nb_elements': 10, 'variable_nb_elements': True, 'value_types': [str], 'allowed_types': [str]}}
    year = {'name': '年'}
    zip = {'name': 'ZIP数据',
           'kwargs': {'uncompressed_size': 256, 'num_files': 32, 'min_file_size': 4,
                      'compression': '```bzip2 | bz2 | lzma | deflate | gzip | gz``` 任选其一'}}

    def kwargs(self) -> dict:
        return copy.deepcopy(self.value.get('kwargs', {}))

    def params_check(self, kwargs=None):
        if kwargs is None:
            kwargs = {}
        for k, v in kwargs.items():
            if type(v) != type(kwargs[k]):
                raise ValueError(f"参数 {k} 类型不匹配，期望类型：{type(v)}，实际类型：{type(kwargs[k])}")
        return True

    def set_kwargs(self, kwargs: dict):
        self.value['kwargs'] = kwargs
        return self

    def title(self):
        return self.value['name']

    def set_title(self, title: str):
        self.value['name'] = title
        return self

    @classmethod
    def names(cls):
        return [ls.name for ls in cls]

    @classmethod
    def values(cls):
        return [ls.value for ls in cls]


class FakerUtils:
    default_config = {
        'locale': 'zh_CN',
        'providers': [
            {
                'method': 'name_',
                'name': '姓名',
                'kwargs': {}
            },
            {
                'method': 'address',
                'name': '地址'
            },
            {
                'method': 'phone_number',
                'name': '手机号'
            },
            {
                'method': 'company',
                'name': '公司'
            },
            {
                'method': 'job',
                'name': '职业'
            },
            {
                'method': 'date_time',
                'name': '日期时间'
            }
        ]
    }

    def __init__(self):
        self.config_file = CONFIG_DIR / 'faker.json'
        self.config_file.touch()
        self.init_config()

    def set_locale(self, locale: str):
        self.fake = Faker(locale)
        config = self.get_config()
        config['locale'] = locale
        self.set_config(config)

    def set_providers(self, providers: List[str]):
        config = self.get_config()
        config['providers'] = providers
        self.set_config(config)

    def get_providers(self):
        config = self.get_config()
        return config.get('providers', [])

    def add_provider(self, provider: dict):
        config = self.get_config()
        providers = config.get('providers', [])
        if provider not in providers:
            providers.append(provider)
            config['providers'] = providers
            self.set_config(config)

    def edit_provider(self, provider: dict, new_provider: dict):
        config = self.get_config()
        providers = config.get('providers', [])
        if provider in providers:
            index = providers.index(provider)
            providers[index] = new_provider
            config['providers'] = providers
            self.set_config(config)

    def remove_provider(self, provider: dict):
        config = self.get_config()
        providers = config.get('providers', [])
        if provider in providers:
            providers.remove(provider)
            config['providers'] = providers
            self.set_config(config)

    def set_config(self, config: dict):
        self.config_file.write_text(json.dumps(config, indent=4, ensure_ascii=False))

    def get_config(self) -> dict:
        try:
            config = json.loads(self.config_file.read_text())
        except:
            self.config_file.write_text(json.dumps(self.default_config, indent=4, ensure_ascii=False))
            config = self.default_config
        return config

    def init_config(self):
        config = self.get_config()
        self.set_locale(config.get('locale', 'zh_CN'))

    def _str_to_method(self, method: Union[str, FakerMethod]) -> FakerMethod:
        """
        字符串转方法
        :param method:
        :return:
        """
        if isinstance(method, str):
            method = FakerMethod[method]
        return method

    def get_kwargs(self, method: Union[str, FakerMethod]) -> dict:
        """
        获取方法的 kwargs
        :param method:
        :return:
        """
        return self._str_to_method(method).kwargs()

    def params_check(self, method: Union[str, FakerMethod], kwargs=None):
        """
        参数检查
        :param method:
        :param args:
        :param kwargs:
        :return:
        """
        return self._str_to_method(method).params_check(kwargs)

    def run(self, method: Union[str, FakerMethod], *args, **kwargs):
        """
        运行方法
        :param method:
        :param args:
        :param kwargs:
        :return:
        """
        name: str = self._str_to_method(method).name
        data = getattr(self.fake, name)(*args, **kwargs)
        if type(data) == datetime:
            data = data.strftime('%Y-%m-%d %H:%M:%S')
        return str(data)

    def generate_data(
            self,
            methods: List[Union[FakerMethod, str]],
            columns: List[str] = None,
            count: int = 1
    ) -> List[dict]:
        """
        批量生成数据
        :param methods: 方法列表
        :param columns: 列名列表
        :param count: 数据条数
        :return:
        """
        if len(methods) != len(columns) and columns is not None:
            raise ValueError("methods 与 columns 长度不匹配")
        data_list = []
        for i in range(count):
            data = {}
            for index, method in enumerate(methods):
                if isinstance(method, str):
                    try:
                        method = FakerMethod[method]
                    except:
                        method = FakerMethod[method + '_']
                if columns is None:
                    column = method.title()
                else:
                    column = columns[index]
                data[column] = self.run(method, **method.kwargs())
            data_list.append(data)
        return data_list

    def generate_data_to_dataframe(
            self,
            methods: List[Union[FakerMethod, str]],
            columns: List[str] = None,
            count: int = 1
    ) -> pd.DataFrame:
        """
        批量生成数据到dataframe
        :param methods:
        :param columns:
        :param count:
        :return:
        """
        return pd.DataFrame(
            self.generate_data(methods, columns, count)
        )

    def generate_data_to_csv(
            self,
            methods: List[Union[FakerMethod, str]],
            columns: List[str] = None,
            count: int = 1,
            file_path: str = 'output.csv'
    ):
        """
        批量生成数据到csv文件
        :return:
        """
        self.generate_data_to_dataframe(methods, columns, count).to_csv(file_path, index=False)

    def generate_data_to_excel(
            self,
            methods: List[Union[FakerMethod, str]],
            columns: List[str] = None,
            count: int = 1,
            file_path: str = 'output.xlsx'
    ):
        """
        批量生成数据到excel文件
        :return:
        """
        self.generate_data_to_dataframe(methods, columns, count).to_excel(file_path, index=False)

    def generate_data_to_json(
            self,
            methods: List[Union[FakerMethod, str]],
            columns: List[str] = None,
            count: int = 1,
            file_path: str = 'output.json'
    ):
        """
        批量生成数据到json文件
        :return:
        """
        self.generate_data_to_dataframe(methods, columns, count).to_json(file_path, orient='records')

    def generate_data_to_yaml(
            self,
            methods: List[Union[FakerMethod, str]],
            columns: List[str] = None,
            count: int = 1,
            file_path: str = 'output.yaml'
    ):
        """
        批量生成数据到yaml文件
        :return:
        """
        data = self.generate_data(methods, columns, count)
        with open(file_path, 'w', encoding='utf-8') as f:
            yaml.safe_dump(data, f, allow_unicode=True)


if __name__ == '__main__':
    faker_utils = FakerUtils()
    for i in faker_utils.default_config['providers']:
        print(faker_utils.run(i))
